import Axios from "axios";
import {WebOfficeInterface} from "./types/WebOfficeInterface";
import {BookmarkRange, CellRange, Dictionary} from "./types/Types";
import {buildFullUrl, isEmpty, safeToString} from "./function/CommonUtils";

export abstract class AbstractWebOffice implements WebOfficeInterface {
    _ready: boolean;
    private _lastClickTime: number = new Date().getTime();
    protected _officeApp: any;
    protected _pluginObj: any;
    protected _activeDocument: any;
    protected _bookmarkRange: Dictionary = {};
    protected _cellRangeCache: Dictionary = {};
    protected _lowerCaseNameToOriginMap: Dictionary={};
    protected _protectPass: string;

    protected _protectionType: number = -1;


    protected constructor(wrapperId: string, public options: Dictionary) {
        const self = this;
        this._protectPass = Math.random() * 10000 + "";
        const objId = self.options.tagId || "PageOfficeCtrl1";
        const wrapperEle: HTMLElement | null = document.getElementById(wrapperId);
        if (wrapperEle == null)
            throw new Error("id为" + wrapperId + "的元素不存在!");
        wrapperEle.insertAdjacentHTML('beforeend', this._createObjectHtml(objId));
        self._pluginObj = document.getElementById(objId);
        self._pluginObj.style.height = wrapperEle.clientHeight;
        self.hide();
    }

    protected abstract _createObjectHtml(objId: string): string;


    private _bookmarkIsInTable(bookmark: any) {
        // var startTime=new Date().getTime();
        const start = bookmark.Start;
        const end = bookmark.End;
        const tables = this.activeDocument().Tables;
        const tableCount = tables.Count;
        if (tableCount > 0) {
            for (let i = 1; i <= tableCount; i++) {
                const vbaTableRange = tables.Item(i).Range;
                const tableStart = vbaTableRange.Start;
                const tableEnd = vbaTableRange.End;
                if (start >= tableStart && start <= tableEnd && end <= tableEnd) {
                    // console.log("bookmarkIsInTable use"+(new Date().getTime()-startTime)+" ms");
                    return true;
                }
            }
        }
        // console.log("bookmarkIsInTable use"+(new Date().getTime()-startTime)+" ms");
        return false;
    }

    protected _cacheBookmarkRange(bookmarkName: string, bookmark: any) {
        this._bookmarkRange = this._bookmarkRange || {};
        const name = safeToString(bookmarkName).toLowerCase();
        this._lowerCaseNameToOriginMap[name]=bookmarkName;
        const bookmarkCache = this._bookmarkRange[name] || {};
        if (bookmark) {
            this._bookmarkRange[name] = bookmarkCache;
            bookmarkCache.range = bookmark.Range;
        }
        return bookmarkCache;
    }

    protected _initDocumentFields() {
        let fields: Dictionary<BookmarkRange> = {};
        if (this.options.document && this.options.document.bookmarkRangeDict) {
            fields = this.options.document.bookmarkRangeDict;
        }
        const bookmarks = this.activeDocument().Bookmarks;
        const bookmarkCount = bookmarks.Count;
        for (let i = 1; i <= bookmarkCount; i++) {
            const bookmark = bookmarks.Item(i);
            if (!bookmark)
                continue;
            const bookmarkName = bookmark.Name;
            this._cacheBookmarkRange(bookmarkName, bookmark);
            if (fields[bookmarkName]) {
                this._initEditors(fields[bookmarkName]);
            }
        }

    }


    protected _initCellRangeList() {
        const that = this;
        let cellRangeList: Array<CellRange> = new Array<CellRange>();
        if (that.options.document && that.options.document.cellRangeList) {
            cellRangeList = that.options.document.cellRangeList;
        }
        this._bookmarkRange = that._bookmarkRange || {};
        for (let i = 0; i < cellRangeList.length; i++) {
            const cellRange: CellRange = cellRangeList[i];
            let bookmarkCache = that._bookmarkRange[safeToString(cellRange.name).toLowerCase()];
            let cellLocationArr = new Array();
            cellLocationArr.push(cellRange.tableIndex, cellRange.rowIndex, cellRange.cellIndex)
            if (!bookmarkCache) {
                let bookmark = that.getBookmark(cellRange.name);
                if (!bookmark) {
                    try {
                        const table = that.activeDocument().Tables.Item(cellRange.tableIndex);
                        if (!table)
                            continue;
                        const cell = table.Cell(cellRange.rowIndex, cellRange.cellIndex);
                        if (!cell)
                            continue;
                        cell.Range.Select();
                        bookmark = that.addBookmark(cellRange.name);
                    }catch (e) {
                        continue;
                    }
                }
                bookmarkCache = that._bookmarkRange[safeToString(cellRange.name).toLowerCase()];
            }
            if(bookmarkCache) {
                that._cellRangeCache[cellLocationArr.join("-")] = bookmarkCache.range;
                that._initEditors(cellRange);
            }
        }

    }

    protected _initEditors(bookmarkRange: BookmarkRange) {
        if (bookmarkRange.value) {
            this.setBookmarkValue(bookmarkRange.name, bookmarkRange.value);
        }
        const bookmarkCache=this._bookmarkRange[bookmarkRange.name.toLowerCase()];
        if (bookmarkRange.editing && this.options.openMode == "docSubmitForm") {
            if (!bookmarkCache.editor) {
                const editors = bookmarkCache.range.Editors;
                if (editors.Count == 0) {
                    bookmarkCache.editor = editors.Add(-1);
                } else {
                    bookmarkCache.editor = editors.Item(1);
                }
                bookmarkCache.range = bookmarkCache.editor.Range;
            }
        }

    }

    public show(): void {
        this.pluginObj().style.height = "100%";
        this.pluginObj().style.position = "static";
    }

    public hide(): void {
        this.pluginObj().style.height = this.pluginObj().parentNode.clientHeight + "px";
        this.pluginObj().style.position = "absolute"
        this.pluginObj().style.left = "-99999px";
    }


    public acceptAllRevisions(): void {
        const that = this;
        that.activeDocument().AcceptAllRevisions();
    }

    public rejectAllRevisions(): void {
        const that = this;
        that.activeDocument().RejectAllRevisions();
    }

    public addBookmark(bookmarkName: string): any {
        const that = this;
        const selection: any = that.officeApp().Selection;
        let markRange = selection.Range;
        const newName = this._bookmarkNameAdapter(bookmarkName);
        if(that.options.extendBookmarks&&bookmarkName.indexOf(that.options.excludeExtendPrefix)===-1)
            markRange = this._computeExtendRange(markRange);
        markRange.Select();
        let bookmark = that.activeDocument().Bookmarks.Add(newName);
        this._cacheBookmarkRange(bookmarkName, bookmark);
        selection.Start = markRange.End;
        return bookmark;

    }

    private _computeExtendRange(markRange: any) {
        //如果需要自动扩展，则在单元格里面只能有一个书签，其他书签会被删除
        let cellsInRange = markRange.Rows?markRange.Cells:null;
        if (cellsInRange && cellsInRange.Count > 0) {
            const cellRange = cellsInRange.Item(cellsInRange.Count).Range;
            const bookmarkText = markRange.Text.replace(/[\s|\n|\r|\t|\x07]/g, "");
            const cellText = cellRange.Text.replace(/[\s|\n|\r|\t|\x07]/g, "");
            if (bookmarkText == cellText && (markRange.Start > cellRange.Start || markRange.End < cellRange.End)) {
                markRange = cellRange;
            }
        }
        if (markRange.Start == markRange.End) {
            markRange.Text = "   ";
        }
        return markRange;
    }

    public setBookmarkValue(bookmarkName: string, value: string | undefined): void {
        const that = this;
        if (!value)
            value = "";
        const bookmarkCache = that._bookmarkRange[safeToString(bookmarkName).toLowerCase()];
        let range;
        if (bookmarkCache && bookmarkCache.range) {
            range = bookmarkCache.range;
        } else {
            let bookmark = that.getBookmark(bookmarkName);
            if (!bookmark)
                throw new TypeError("书签[" + bookmarkName + "]不存在！");
            bookmark.Select();
            bookmark = that.addBookmark(bookmarkName);
            range = bookmark.Range;
        }
        range = this.activeDocument().Range(range.Start, range.End);
        range.Text = value;
        if (that._protectionType == -1 && !that.isBookmarkExists(bookmarkName)) {
            const realName = this._bookmarkNameAdapter(bookmarkName);
            range.Select();
            const bookmark=that.activeDocument().Bookmarks.Add(realName);
            this._cacheBookmarkRange(bookmarkName, bookmark);
            that.officeApp().Selection.Range.Start = range.End;
        }
    }

    abstract createFile(): Promise<any>;

    public deleteBookmark(name: string): void {
        const that = this;
        const bookmark = that.getBookmark(this._bookmarkNameAdapter(name));
        if (bookmark) {
            bookmark.Delete();
        }
    }

    abstract enableToolbars(enable: boolean): void;

    abstract getAllBookmarkValue(): Object;

    public getBookmarkValue(bookmarkName: string): string | undefined {
        const that = this;
        const bookmarkCache = that._bookmarkRange[safeToString(bookmarkName).toLowerCase()];
        let result: string = "", range: any;
        if (bookmarkCache && bookmarkCache.range) {
            range = bookmarkCache.range;
            // result=this.app.Application.CleanString(range.Text);
        } else {
            const bookmark = that.getBookmark(bookmarkName);
            if (bookmark) {
                range = bookmark.Range;
            }
        }
        result = range.Text;
        result = safeToString(result);
        result = result.replace(/\x07$/g, "").replace(/\x0d$/g, "");
        return result;
        // console.log("书签《" + bookmarkName + "》取值用时：" + (new Date().getTime() - startTime) + "ms");

    }


    public officeApp(): any {
        return this._officeApp;
    }

    public pluginObj(): any {
        return this._pluginObj;
    }

    public activeDocument(): any {
        if (isEmpty(this._activeDocument))
            this._activeDocument = this.officeApp().ActiveDocument;
        if (this._activeDocument != this.officeApp().ActiveDocument) {
            try{
                this._activeDocument.Activate();
            }catch (e) {
                this._activeDocument = this.officeApp().ActiveDocument;
            }
        }
        return this._activeDocument;
    }


    abstract importFile(): Promise<any>;

    public insertDocumentFromUrl(documentUrl: string): Promise<boolean> {
        const that = this;
        return new Promise(function (resolve) {
            that.officeApp().Selection.InsertFile(buildFullUrl(documentUrl));
            resolve(true);
        });
    }

    public insertImage(imageUrl: string, pageNo: number = 0, left: number = -999999, top: number = -999999, width: number = 0, height: number = 0): void {
        const result = this._insertImageFromUrlByVBA(imageUrl, pageNo, left, top, width, height);
        if (result == 0) {
            const oDoc = this.activeDocument();
            let mLeft: number = oDoc.PageSetup.LeftMargin, mTop: number = oDoc.PageSetup.TopMargin,
                mRight: number = oDoc.PageSetup.RightMargin, mBottom = oDoc.PageSetup.BottomMargin,
                pageHeight: number = oDoc.PageSetup.PageHeight, pageWidth: number = oDoc.PageSetup.PageWidth;
            if (mTop > pageHeight)
                mTop = 72;
            if (mLeft > pageWidth)
                mLeft = 90;
            if (mBottom > pageHeight)
                mBottom = 72;
            if (mRight > pageWidth)
                mRight = 90;
            let pWidth: number = this.officeApp().MillimetersToPoints(width),
                pHeight: number = this.officeApp().MillimetersToPoints(height),
                pLeft: number = this.officeApp().MillimetersToPoints(left),
                pTop: number = this.officeApp().MillimetersToPoints(top);
            //获取总页数
            const pageCount = this.officeApp().ActiveWindow.Panes.Item(1).Pages.Count;
            //清理之前已经存在图片
            for (let i = 1; i <= pageCount; i++) {
                const shapesC = oDoc.Shapes.Count;
                for (let j = 1; j <= shapesC; j++) {
                    const oShape = oDoc.Shapes.Item(j);
                    if (oShape != null && (oShape.Name == imageUrl + i || oShape.Name == imageUrl))
                        oShape.Delete()
                }
            }
            let oShape = oDoc.Shapes.AddPicture(imageUrl, false, true);
            if (pWidth == 0)
                pWidth = oShape.Width;
            if (pHeight == 0)
                pHeight = oShape.Height;
            if (pLeft < 0)
                pLeft = pageWidth - mLeft - mRight - pWidth + pLeft;
            if (pTop < 0)
                pTop = pageHeight - mTop - mBottom - pHeight + pTop;

            if (left == -999995)
                pLeft = (pageWidth - mLeft - mRight - pWidth) / 2;
            if (top == -999995)
                pTop = (pageHeight - mTop - mBottom - pHeight) / 2;
            if (pageNo == 0) {
                oShape.Name = imageUrl;
                oShape.LockAspectRatio = true;
                oShape.LayoutInCell = false;
                if(left==-999999||top==-999999){
                    oShape.Anchor.Copy();
                    this.officeApp().Selection.Range.Paste();
                    oShape.Delete();
                    return;
                }
                //图片的水平位置，相对于边距   单位 磅\n" +
                oShape.RelativeHorizontalPosition = 0;
                //图片的垂直位置，相对于边距   单位 磅\n" +
                oShape.RelativeVerticalPosition = 0;
                oShape.Left = pLeft;
                oShape.Top = pTop;
                return;
            } else {
                oShape.Delete();
            }
            let startPage: number = pageNo, endPage: number = pageNo;
            if (pageNo == -1) {
                startPage = pageCount;
                endPage = pageCount;
            } else if (pageNo == -2) {
                startPage = 1;
                endPage = pageCount;
            }
            for (let i = startPage; i <= endPage; i++) {
                const oRang = oDoc.GoTo(1, 1, i);
                oShape = oDoc.Shapes.AddPicture(imageUrl, false, true, pLeft, pTop, pWidth, pHeight, oRang);
                oShape.Name = imageUrl + i;
                //图片的水平位置，相对于边距   单位 磅\n" +
                oShape.RelativeHorizontalPosition = 0;
                //图片的垂直位置，相对于边距   单位 磅\n" +
                oShape.RelativeVerticalPosition = 0;
                oShape.Left = pLeft;
                oShape.Top = pTop;
                oShape.LockAspectRatio = true;
                oShape.LayoutInCell = false;
            }
        }
    }

    private _insertImageFromUrlByVBA(imageUrl: string, pageNo: number, left: number, top: number, width: number, height: number): number {
        return this.runMacro("CallInsertPic", "Function CallInsertPic() As Boolean\n" +
            "CallInsertPic = InsertPic(\"" + imageUrl + "\"," + pageNo + "," + left + "," + top + "," + width + "," + height + ")\n" +
            "End Function\n" +
            "\n" +
            "Function InsertPic(picPath As String, Optional pageNo As Single, Optional picLeft As Single, Optional picTop As Single, Optional picWidth As Single, Optional picHeight As Single) As Boolean\n" +
            "    \n" +
            "    Dim pWidth, pHeight, pLeft, pTop  As Single    '图片位置大小信息\n" +
            "    Dim mRight, mBottom, mLeft, mTop As Single  '页边距\n" +
            "    Dim pageHeight, pageWidth As Single 'word页面大小(页边距以内)\n" +
            "     \n" +
            "    Set oDoc = ActiveDocument\n" +
            "    '获取页边距\n" +
            "    mLeft = oDoc.PageSetup.LeftMargin\n" +
            "    mTop = oDoc.PageSetup.TopMargin\n" +
            "     \n" +
            "    '页面大小\n" +
            "    pageHeight = oDoc.PageSetup.PageHeight\n" +
            "    pageWidth = oDoc.PageSetup.PageWidth\n" +
            "    mRight = ActiveDocument.PageSetup.RightMargin\n" +
            "    mBottom = ActiveDocument.PageSetup.BottomMargin\n" +
            "    \n" +
            "    If mTop > pageHeight Then\n" +
            "        mTop = 72\n" +
            "    End If\n" +
            "    If mLeft > pageWidth Then\n" +
            "        mLeft = 90\n" +
            "    End If\n" +
            "    If mBottom > pageHeight Then\n" +
            "        mBottom = 72\n" +
            "    End If\n" +
            "    If mRight > pageWidth Then\n" +
            "        mRight = 90\n" +
            "    End If\n" +
            "    '计算单位，从毫米到磅\n" +
            "    'p2cUnit = 2.835     '1毫米大约等于2.835磅\n" +
            "    pWidth = Application.MillimetersToPoints(picWidth)\n" +
            "    pHeight = Application.MillimetersToPoints(picHeight)\n" +
            "    pLeft = Application.MillimetersToPoints(picLeft)\n" +
            "    pTop = Application.MillimetersToPoints(picTop)\n" +
            "    If picLeft = wdShapeCenter Then\n" +
            "        pLeft = wdShapeCenter\n" +
            "    End If\n" +
            "    If picTop = wdShapeCenter Then\n" +
            "        pTop = wdShapeCenter\n" +
            "    End If\n" +
            "   \n" +
            "    '获取总页数\n" +
            "    pageCount = ActiveDocument.ComputeStatistics(wdStatisticPages, False)\n" +
            "     \n" +
            "    '清理之前已经存在图片\n" +
            "        On Error Resume Next\n" +
            "    Set oShape = oDoc.Shapes.Item(picPath)\n" +
            "        If Not oShape Is Nothing Then\n" +
            "            oShape.Delete\n" +
            "        End If\n" +
            "        Err.Clear\n" +
            "    For pIndex = 1 To pageCount\n" +
            "        On Error Resume Next\n" +
            "        Set oShape = oDoc.Shapes.Item(picPath & pIndex)\n" +
            "         \n" +
            "        If Not oShape Is Nothing Then\n" +
            "            oShape.Delete\n" +
            "        End If\n" +
            "        Err.Clear\n" +
            "    Next\n" +
            "       Set oShape=ActiveDocument.Shapes.AddPicture(FileName:=picPath, LinkToFile:=False, SaveWithDocument:=True,Anchor:=Selection.Range)\n"+
            "       If pWidth=0 Then\n"+
            "           pWidth=oShape.Width\n"+
            "       End If\n"+
            "       If pHeight=0 Then\n"+
            "           pHeight=oShape.Height\n"+
            "       End If\n"+
            "    If pLeft < 0 And pLeft <> wdShapeCenter Then\n" +
            "        pLeft = pageWidth - mLeft -mRight- pWidth + pLeft\n" +
            "    End If\n" +
            "    If pTop < 0 And pTop <> wdShapeCenter Then\n" +
            "        pTop = pageHeight - mTop - mBottom- pHeight + pTop\n" +
            "    End If\n" +
            "    If pageNo = 0  Then\n" +
            "        oShape.Name = picPath\n" +
            "        If picLeft=-999999 or picTop=-999999 Then\n"+
            "           pLeft=Selection.Information(wdHorizontalPositionRelativeToPage)\n" +
            "           pTop=Selection.Information(wdVerticalPositionRelativeToPage)\n" +
            "         End If\n" +
            "        '图片的水平位置，相对于边距   单位 磅\n" +
            "        oShape.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin\n" +
            "        '图片的垂直位置，相对于边距   单位 磅\n" +
            "        oShape.RelativeVerticalPosition = wdRelativeVerticalPositionMargin\n" +
            "        oShape.Left = pLeft\n" +
            "        oShape.Top = pTop\n" +
            "        oShape.LockAspectRatio = True\n" +
            "        oShape.LayoutInCell = False\n" +
            "        InsertPic = True\n" +
            "        Exit Function\n" +
            "    Else\n"+
            "        oShape.Delete\n"+
            "    End If\n" +
            "    startPage = pageNo\n" +
            "    endPage = pageNo\n" +
            "     If pageNo = -1 Then\n" +
            "        startPage = pageCount\n" +
            "        endPage = pageCount\n" +
            "     End If\n" +
            "     If pageNo = -2 Then\n" +
            "        startPage = 1\n" +
            "        endPage = pageCount\n" +
            "     End If\n" +
            "     Set curRange = Selection.Range\n" +
            "    '遍历每页添加图片\n" +
            "    For pIndex = startPage To endPage\n" +
            "        Set oRang = oDoc.GoTo(wdGoToPage, wdGoToAbsolute, pIndex)\n" +
            "        Set oShape = oDoc.Shapes.AddPicture(picPath, False, True, pLeft, pTop, pWidth, pHeight, oRang)\n" +
            "        oShape.Name = picPath & pIndex\n" +
            "         \n" +
            "        '图片的水平位置，相对于边距   单位 磅\n" +
            "        oShape.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin\n" +
            "        '图片的垂直位置，相对于边距   单位 磅\n" +
            "        oShape.RelativeVerticalPosition = wdRelativeVerticalPositionMargin\n" +
            "        oShape.Left = pLeft\n" +
            "        oShape.Top = pTop\n" +
            "        oShape.LockAspectRatio = True\n" +
            "        oShape.LayoutInCell = False\n" +
            "    Next\n" +
            "    curRange.Select\n" +
            "     InsertPic = True\n" +
            "End Function");
    }

    public isBookmarkExists(bookmark: string): boolean {
        return this.activeDocument().Bookmarks.Exists(this._bookmarkNameAdapter(bookmark));
    }

    protected _bookmarkNameAdapter(bookmarkName: string) {
        return bookmarkName;
    }

    public getBookmark(bookmarkName: string) {
        const newName = this._bookmarkNameAdapter(bookmarkName);
        if (this.isBookmarkExists(newName)) {
            return this.activeDocument().Bookmarks.Item(newName);

        }
        return null;
    }

    public locateBookmark(bookmarkName: string): void {
        const that = this;
        const bookmark = that.getBookmark(this._bookmarkNameAdapter(bookmarkName));
        if (bookmark) {
            bookmark.Select();
        }
    }

    public abstract openLocalFile(filePath: string, readOnly?: boolean): Promise<any>;

    abstract openRemoteFile(url: string, readOnly: boolean): Promise<any>;

    abstract print(): void;

    public runMacro(method: string, command?: string): any {
        if (this._officeApp == null || !this.activeDocument().VBProject)
            return 0;
        let result: any;
        if (command) {
            const module = this.activeDocument().VBProject.VBComponents.Add(1);
            module.CodeModule.AddFromString(command);
            result = this._officeApp.Run(method);
            this.activeDocument().VBProject.VBComponents.Remove(module);
        } else {
            result = this._officeApp.Run(method);
        }
        return result;
    };

    public getCellText(tableIndex: number, rowIndex: number, cellIndex: number): string | undefined {
        const cellRange = this._getCellRange(tableIndex, rowIndex, cellIndex);
        let result = cellRange.Text;
        result = safeToString(result);
        result = result.replace(/\x07$/g, "").replace(/\x0d$/g, "");
        return result;
    }

    private _getCellRange(tableIndex: number, rowIndex: number, cellIndex: number) {
        let cellRange = this._cellRangeCache[tableIndex + "-" + rowIndex + "-" + cellIndex];
        if (!cellRange) {
            cellRange = this.activeDocument().Tables.Item(tableIndex).Cell(rowIndex, cellIndex).Range;
            this._cellRangeCache[tableIndex + "-" + rowIndex + "-" + cellIndex] = cellRange;
        }
        return cellRange;
    }

    public setCellText(tableIndex: number, rowIndex: number, cellIndex: number, text: string): void {
        if (!text)
            text = "";
        let cellRange = this._getCellRange(tableIndex, rowIndex, cellIndex);
        cellRange = this.activeDocument().Range(cellRange.Start, cellRange.End);
        cellRange.Text = text;
    }

    abstract saveAs(fileName?: string): void

    abstract saveToUrl(tempUrl?: string): Promise<any>;


    public trackRevisions(enable: boolean = true): void {
        const that = this;
        if(that._protectionType>0) {
            that.activeDocument().TrackRevisions = enable;
        }else if(that.activeDocument().TrackRevisions!==enable){
            if(enable){
                that.activeDocument().Protect(0, true, that._protectPass);
            }else {
                that.activeDocument().Unprotect(that._protectPass);
            }
        }else {
            that.activeDocument().TrackRevisions=enable;
        }
    }

    public showRevisions(show: boolean = true): void {
        const that = this;
        that.activeDocument().ShowRevisions = show;
        that.activeDocument().ActiveWindow.View.ShowRevisionsAndComments = show;

    }

    protected _extendBookmarks() {
        const result = this._extendBookmarksByVBA();
        if (result != 0)
            return;
        const bookmarks = this.activeDocument().Bookmarks;
        const bookmarkCount = bookmarks.Count;
        for (let i = 1; i <= bookmarkCount; i++) {
            let bookmark = bookmarks.Item(i);
            if (!bookmark)
                continue;
            const bookmarkName=bookmark.Name;
            if(bookmarkName.indexOf(this.options.excludeExtendPrefix)===0)
                continue;
            let markRange = this._computeExtendRange(bookmark.Range);
            if (bookmark.Start === markRange.Start && bookmark.End === markRange.End)
                continue;
            markRange.Select();
            bookmark = this.activeDocument().Bookmarks.Add(bookmarkName);
        }

    }

    protected _extendBookmarksByVBA() {
        return this.runMacro("extendBookmarks", "Sub extendBookmarks()\n" +
            "    Dim bkCount As Integer\n" +
            "    bkCount = ActiveDocument.Bookmarks.Count\n" +
            "    Dim x As Integer\n" +
            "    Set reg = CreateObject(\"vbscript.regexp\")\n" +
            "    reg.IgnoreCase = True\n" +
            "    reg.Pattern = \"[\\x09\\x0A\\x0d\\x07]\"\n" +
            "    reg.Global = True\n" +
            "    Dim textInCell, bookmarkText As String\n" +
            "    Dim bookmarkFullOfCell As Boolean\n" +
            "    For x = 1 To bkCount Step 1\n" +
            "        bookmarkFullOfCell = False\n" +
            "        Set bookmarkObj = ActiveDocument.Bookmarks.Item(x)\n" +
            "        If instr(1,bookmarkObj.Name,\""+this.options.excludeExtendPrefix+"\")=0 Then\n"+
            "        bookmarkText = bookmarkObj.Range.Text\n" +
            "        Set cellsInBookmark = bookmarkObj.Range.Cells\n" +
            "        If IsNull(cellsInBookmark) <> True And Not cellsInBookmark Is Nothing Then\n" +
            "            Set singleCell = cellsInBookmark.Item(1)\n" +
            "            textInCell = singleCell.Range.Text\n" +
            "            If bookmarkObj.Start = singleCell.Range.Start And bookmarkObj.End = singleCell.Range.End Then\n" +
            "                bookmarkFullOfCell = True\n" +
            "            End If\n" +
            "            '如果书签外没有其他内容，则该书签独占单元格的所有空间，如果单元格中还有其他书签，则会被删除掉\n" +
            "            If Trim(reg.Replace(textInCell, \"\")) = Trim(reg.Replace(bookmarkText, \"\")) And bookmarkFullOfCell = False Then\n" +
            "                Set matchs = reg.Execute(textInCell)\n" +
            "                textInCell = reg.Replace(textInCell, \"\")\n" +
            "                If Len(Trim(textInCell)) = 0 And matchs.Count > 2 Then\n" +
            "                    textInCell = Chr(13) + textInCell\n" +
            "                    singleCell.Range.Text = textInCell\n" +
            "                End If\n" +
            "                Set bookmarkObj = ActiveDocument.Bookmarks.Add(bookmarkObj.Name, singleCell.Range)\n" +
            "                bookmarkFullOfCell = True\n" +
            "                bookmarkText = textInCell\n" +
            "            End If\n" +
            "        End If\n" +
            "        If bookmarkObj.Start = bookmarkObj.End Or (bookmarkFullOfCell = False And Right(reg.Replace(bookmarkText, \"\"), 1) <> \" \") Then\n" +
            "            Set bRange = bookmarkObj.Range\n" +
            "            bRange.Text = reg.Replace(bookmarkText, \"\") + Chr(32)\n" +
            "            If bookmarkFullOfCell = False Then\n" +
            "                Set bookmarkObj = ActiveDocument.Bookmarks.Add(bookmarkObj.Name, bRange)\n" +
            "            End If\n" +
            "        End If\n" +
            "        End If\n" +
            "    Next x\n" +
            "End Sub");
    }

    public addWordImageWaterMark(imagePath: string, left: number = -999995, top: number = -999995, width: number = 0, height: number = 0, rotation: number = 0): void {
        const result: number = this._addWordImageWaterMark(imagePath, left, top, width, height, rotation);
        if (result == 0) {
            const ActiveDocument = this.activeDocument();
            //获取页边距
            let mLeft: number = ActiveDocument.PageSetup.LeftMargin, mTop = ActiveDocument.PageSetup.TopMargin
                , mRight: number = ActiveDocument.PageSetup.RightMargin,
                mBottom = ActiveDocument.PageSetup.BottomMargin;
            //页面大小
            const pageHeight: number = ActiveDocument.PageSetup.PageHeight,
                pageWidth: number = ActiveDocument.PageSetup.PageWidth;
            width = this.officeApp().MillimetersToPoints(width);
            height = this.officeApp().MillimetersToPoints(height);
            if (mTop > pageHeight)
                mTop = 72;
            if (mLeft > pageWidth)
                mLeft = 90;
            if (mBottom > pageHeight)
                mBottom = 72;
            if (mRight > pageWidth)
                mRight = 90;
            if (left != -999995)
                left = this.officeApp().MillimetersToPoints(left);
            if (top != -999995)
                top = this.officeApp().MillimetersToPoints(top);
            if (left < 0 && left != -999995)
                left = pageWidth - mLeft - mRight - width + left;
            if (top < 0 && top != -999995)
                top = pageHeight - mTop - mBottom - height + top;
            if (width == 0 || height == 0) {
                const tShape = ActiveDocument.Shapes.AddPicture(imagePath, false, true);
                if (width == 0)
                    width = tShape.Width;
                if (height == 0)
                    height = tShape.Height;
                tShape.Delete();
            }
            if (left == -999995)
                left = (pageWidth - mLeft - mRight - width) / 2
            if (top == -999995)
                top = (pageHeight - mTop - mBottom - height) / 2
            this.officeApp().ActiveWindow.ActivePane.View.SeekView = 9;
            const oShape = this.officeApp().Selection.HeaderFooter.Shapes.AddPicture(imagePath, false, true, left, top, width, height);
            oShape.Line.Visible = false;
            oShape.RelativeHorizontalPosition = 0;
            oShape.RelativeVerticalPosition = 0;
            oShape.Left = left;
            oShape.Top = top;
            oShape.Name = imagePath;
            oShape.WrapFormat.AllowOverlap = true;
            oShape.WrapFormat.Side = 3;
            oShape.WrapFormat.Type = 3;
            oShape.PictureFormat.ColorType = 4;
            if (rotation != 0)
                oShape.Rotation = rotation;

            this.officeApp().ActiveWindow.ActivePane.View.SeekView = 0;
        }
    }

    private _addWordImageWaterMark(imagePath: string, left: number = -999995, top: number = -999995, width: number = 0, height: number = 0, rotation: number = 0): number {
        return this.runMacro("addImageWaterMark", "Function addImageWaterMark() As Boolean\n" +
            "    addImageWaterMark = addWordImageWaterMark(\"" + imagePath + "\", " + left + ", " + top + ", " + width + ", " + height + ", " + rotation + ")\n" +
            "End Function\n" +
            "Function addWordImageWaterMark(imgPath As String, Optional left As Single, Optional top As Single _\n" +
            ", Optional width As Single, Optional height As Single _\n" +
            ", Optional rotation As Single) As Boolean\n" +
            "    '获取页边距\n" +
            "    mLeft = ActiveDocument.PageSetup.LeftMargin\n" +
            "    mTop = ActiveDocument.PageSetup.TopMargin\n" +
            "    mRight = ActiveDocument.PageSetup.RightMargin\n" +
            "    mBottom = ActiveDocument.PageSetup.BottomMargin\n" +
            "     \n" +
            "    '页面大小\n" +
            "    pageHeight = ActiveDocument.PageSetup.PageHeight\n" +
            "    pageWidth = ActiveDocument.PageSetup.PageWidth\n" +
            "    \n" +
            "    '计算单位，从毫米到磅\n" +
            "    'p2cUnit = 2.835     '1毫米大约等于2.835磅\n" +
            "    width = Application.MillimetersToPoints(width)\n" +
            "    height = Application.MillimetersToPoints(height)\n" +
            "    pLeft = Application.MillimetersToPoints(left)\n" +
            "    pTop = Application.MillimetersToPoints(top)\n" +
            "    If mTop > pageHeight Then\n" +
            "        mTop = 72\n" +
            "    End If\n" +
            "    If mLeft > pageWidth Then\n" +
            "        mLeft = 90\n" +
            "    End If\n" +
            "    If mBottom > pageHeight Then\n" +
            "        mBottom = 72\n" +
            "    End If\n" +
            "    If mRight > pageWidth Then\n" +
            "        mRight = 90\n" +
            "    End If\n" +
            "    If left = wdShapeCenter Then\n" +
            "        pLeft = wdShapeCenter\n" +
            "    End If\n" +
            "    If top = wdShapeCenter Then\n" +
            "        pTop = wdShapeCenter\n" +
            "    End If\n" +
            "\n" +
            "    If pLeft < 0 And pLeft <> wdShapeCenter Then\n" +
            "        pLeft = pageWidth - mLeft -mRight- width + pLeft\n" +
            "    End If\n" +
            "    If pTop < 0 And pTop <> wdShapeCenter Then\n" +
            "        pTop = pageHeight - mTop - mBottom- height + pTop\n" +
            "    End If\n" +
            "    If width =0 or height =0 Then\n"+
            "       Set tShape=ActiveDocument.Shapes.AddPicture(imgPath, False, False)\n"+
            "       If width=0 Then\n"+
            "           width=tShape.Width\n"+
            "       End If\n"+
            "       If height=0 Then\n"+
            "           height=tShape.Height\n"+
            "       End If\n"+
            "       tShape.Delete\n"+
            "    End If\n"+
            "    ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader\n" +
            "    Set oShape = Selection.HeaderFooter.Shapes.AddPicture(imgPath, False, True)\n" +
            "    oShape.Width = width\n" +
            "    oShape.Height = height\n" +
            "    oShape.Line.Visible = False\n" +
            "    oShape.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin\n" +
            "    oShape.RelativeVerticalPosition = wdRelativeVerticalPositionMargin\n" +
            "    oShape.Left = pLeft\n" +
            "    oShape.Top = pTop\n" +
            "    oShape.Name = imgPath\n" +
            "    oShape.WrapFormat.AllowOverlap = True\n" +
            "    oShape.WrapFormat.Side = 3\n" +
            "    oShape.WrapFormat.Type = 3\n" +
            "    oShape.PictureFormat.ColorType = 4\n"+
            "    If rotation <> 0 Then\n" +
            "        oShape.Rotation = rotation\n" +
            "    End If\n" +
            "    ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument\n" +
            "    addWordImageWaterMark = True\n" +
            "End Function\n");
    }

    public addWordTextWaterMark(text: string, left: number = -999995, top: number = -999995, fontFamily: string = '宋体', fontSize: number = 30, color: number = 0xd0d0d0, width: number = 0, height: number = 0, rotation: number = 0, percent: number = 0): void {
        const result: number = this._addWordTextWaterMarkByVBA(text, left, top, fontFamily, fontSize, color, width, height, rotation, percent);
        if (result == 0) {
            const ActiveDocument = this.activeDocument();
            //获取页边距
            let mLeft: number = ActiveDocument.PageSetup.LeftMargin, mTop = ActiveDocument.PageSetup.TopMargin
                , mRight: number = ActiveDocument.PageSetup.RightMargin,
                mBottom = ActiveDocument.PageSetup.BottomMargin;
            //页面大小
            const pageHeight: number = ActiveDocument.PageSetup.PageHeight,
                pageWidth: number = ActiveDocument.PageSetup.PageWidth;
            width = this.officeApp().MillimetersToPoints(width);
            height = this.officeApp().MillimetersToPoints(height);
            if (left != -999995)
                left = this.officeApp().MillimetersToPoints(left);
            if (top != -999995)
                top = this.officeApp().MillimetersToPoints(top);
            if (mTop > pageHeight)
                mTop = 72;
            if (mLeft > pageWidth)
                mLeft = 90;
            if (mBottom > pageHeight)
                mBottom = 72;
            if (mRight > pageWidth)
                mRight = 90;
            if (left < 0 && left != -999995)
                left = pageWidth - mLeft - mRight - width + left;
            if (top < 0 && top != -999995)
                top = pageHeight - mTop - mBottom - height + top;
            this.officeApp().ActiveWindow.ActivePane.View.SeekView = 9;
            const oShape = this.officeApp().Selection.HeaderFooter.Shapes.AddTextEffect(0, text, fontFamily, fontSize, false, false, 0, 0);
            if (width != 0)
                oShape.Width = width;
            if (height != 0)
                oShape.Height = height;
            oShape.Line.Visible = false;
            oShape.RelativeHorizontalPosition = 0;
            oShape.RelativeVerticalPosition = 0;
            if (left == -999995)
                left = (pageWidth - mLeft - mRight - oShape.Width) / 2
            if (top == -999995)
                top = (pageHeight - mTop - mBottom - oShape.Height) / 2
            oShape.Left = left;
            oShape.Top = top;
            oShape.Name = text;
            oShape.TextEffect.NormalizedHeight = false;
            oShape.Fill.Visible = true;
            oShape.Fill.Solid();
            oShape.Fill.ForeColor.RGB = color;
            oShape.WrapFormat.AllowOverlap = true;
            oShape.WrapFormat.Side = 3;
            oShape.WrapFormat.Type = 3;
            if (percent != 0)
                oShape.Fill.Transparency = percent / 100;
            if (rotation != 0)
                oShape.Rotation = rotation;
            this.officeApp().ActiveWindow.ActivePane.View.SeekView = 0;
        }
    }


    private _addWordTextWaterMarkByVBA(text: string, left: number, top: number, fontFamily: string, fontSize: number, color: number, width: number, height: number, rotation: number, percent: number): number {
        return this.runMacro("addWaterWark", "Function addWaterWark() As Boolean\n" +
            "    addWaterWark = addWordTextWaterMark(\"" + text + "\", " + left + ", " + top + ", \"" + fontFamily + "\", " + fontSize + ", " + color + ", " + width + ", " + height + ", " + rotation + "," + percent + ")\n" +
            "End Function\n" +
            "Function addWordTextWaterMark(text As String, Optional left As Single, Optional top As Single _\n" +
            ", Optional fontFamily As String, Optional fontSize As Single, Optional color As Single _\n" +
            ", Optional width As Single, Optional height As Single _\n" +
            ", Optional rotation As Single, Optional percent As Single) As Boolean\n" +
            "    '获取页边距\n" +
            "    mLeft = ActiveDocument.PageSetup.LeftMargin\n" +
            "    mTop = ActiveDocument.PageSetup.TopMargin\n" +
            "    mRight = ActiveDocument.PageSetup.RightMargin\n" +
            "    mBottom = ActiveDocument.PageSetup.BottomMargin\n" +
            "     \n" +
            "    '页面大小\n" +
            "    pageHeight = ActiveDocument.PageSetup.PageHeight\n" +
            "    pageWidth = ActiveDocument.PageSetup.PageWidth\n" +
            "    \n" +
            "    '计算单位，从毫米到磅\n" +
            "    'p2cUnit = 2.835     '1毫米大约等于2.835磅\n" +
            "    width = Application.MillimetersToPoints(width)\n" +
            "    height = Application.MillimetersToPoints(height)\n" +
            "    pLeft = Application.MillimetersToPoints(left)\n" +
            "    pTop = Application.MillimetersToPoints(top)\n" +
            "    If mTop > pageHeight Then\n" +
            "        mTop = 72\n" +
            "    End If\n" +
            "    If mLeft > pageWidth Then\n" +
            "        mLeft = 90\n" +
            "    End If\n" +
            "    If mBottom > pageHeight Then\n" +
            "        mBottom = 72\n" +
            "    End If\n" +
            "    If mRight > pageWidth Then\n" +
            "        mRight = 90\n" +
            "    End If\n" +
            "    If left = wdShapeCenter Then\n" +
            "        pLeft = wdShapeCenter\n" +
            "    End If\n" +
            "    If top = wdShapeCenter Then\n" +
            "        pTop = wdShapeCenter\n" +
            "    End If\n" +
            "\n" +
            "    If pLeft < 0 And pLeft <> wdShapeCenter Then\n" +
            "        pLeft = pageWidth - mLeft -mRight- width + pLeft\n" +
            "    End If\n" +
            "    If pTop < 0 And pTop <> wdShapeCenter Then\n" +
            "        pTop = pageHeight - mTop - mBottom- height + pTop\n" +
            "    End If\n" +
            "    If fontFamily = \"\" Then\n" +
            "        fontFamily = \"宋体\"\n" +
            "    End If\n" +
            "    If fontSize = 0 Then\n" +
            "        fontSize = 30\n" +
            "    End If\n" +
            "    If color = 0 Then\n" +
            "        color = &HD0D0D0\n" +
            "    End If\n" +
            "    ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader\n" +
            "    Set oShape = Selection.HeaderFooter.Shapes.AddTextEffect(msoTextEffect1, text, fontFamily, fontSize, msoFalse, msoFalse, pLeft, pTop)\n" +
            "    If width <> 0 Then\n" +
            "        oShape.Width = width\n" +
            "    End If\n" +
            "    If height <> 0 Then\n" +
            "        oShape.Height = height\n" +
            "    End If\n" +
            "    oShape.Line.Visible = False\n" +
            "    oShape.RelativeHorizontalPosition = wdRelativeHorizontalPositionMargin\n" +
            "    oShape.RelativeVerticalPosition = wdRelativeVerticalPositionMargin\n" +
            "    oShape.Left = pLeft\n" +
            "    oShape.Top = pTop\n" +
            "    oShape.Name = text\n" +
            "    oShape.TextEffect.NormalizedHeight = False\n" +
            "    oShape.Fill.Visible = True\n" +
            "    oShape.Fill.Solid\n" +
            "    oShape.Fill.ForeColor.RGB = color\n" +
            "    oShape.WrapFormat.AllowOverlap = True\n" +
            "    oShape.WrapFormat.Side = 3\n" +
            "    oShape.WrapFormat.Type = 3\n" +
            "    If percent <> 0 Then\n" +
            "        oShape.Fill.Transparency = percent / 100\n" +
            "    End If\n" +
            "    If rotation <> 0 Then\n" +
            "        oShape.Rotation = rotation\n" +
            "    End If\n" +
            "    \n" +
            "    ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument\n" +
            "    addWordTextWaterMark = True\n" +
            "End Function\n");
    }

    saveDataRange(): Promise<any> {
        const that = this;
        return new Promise<any>(function (resolve, reject) {
            const url = that.options.saveDataPage;
            const data = that.getAllBookmarkValue();
            Axios.post(url, data, {responseType: "json"}).then(response=>{
                resolve(response.data);
            }, reject);
        });
    }

    webSave(): Promise<any> {
        const that = this;
        return new Promise<any>(function (resolve, reject) {
            that.saveToUrl().then(function (result) {
                if (isEmpty(that.options.saveDataPage))
                    resolve(result);
                else
                    that.saveDataRange().then(resolve, reject);
            }, reject);
        });
    }


    protected _recoveryAllEditors() {
        for (const markname in this._bookmarkRange) {
            if (this._bookmarkRange[markname] && this._bookmarkRange[markname].editor) {
                const editorsInRange = this._bookmarkRange[markname].range.Editors;
                if (editorsInRange.Count == 0) {
                    this._bookmarkRange[markname].editor = editorsInRange.Add(-1);
                } else {
                    this._bookmarkRange[markname].editor = editorsInRange.Item(1);
                }
            }
        }
    }

    protected _deleteAllEditors() {
        for (const markname in this._bookmarkRange) {
            if (this._bookmarkRange[markname] && this._bookmarkRange[markname].editor) {
                this._bookmarkRange[markname].editor.DeleteAll();
                break;
            }
        }
    }

    protected _publicDocumentInitialize() {
        const self = this;
        const openMode = self.options.openMode;
        this._bookmarkRange={};
        this._cellRangeCache={};
        if (this.options.extendBookmarks)
            self._extendBookmarks();
        self._initDocumentFields();
        self._initCellRangeList();
        if (openMode == "docNormalEdit") {

        } else if (openMode == "docRevisionOnly") {
            self._protectionType = 0;
        } else if (openMode == "docAdmin") {
            self.activeDocument().AcceptAllRevisions();
            self.showRevisions(true);
        } else {
            self._protectionType = 3;
        }
        if (self._protectionType > -1)
            self.activeDocument().Protect(self._protectionType, true, self._protectPass);
        self.officeApp().Selection.HomeKey(6);
    }

    _onWordWpsSelChange(selection: any) {
        const self = this;
        const currentTime = new Date().getTime();
        if (currentTime - self._lastClickTime < 1000) {
            self._lastClickTime = currentTime;
            return;
        }
        self._lastClickTime = currentTime;
        if (self.options.jsFunction_OnWordDataRegionClick && (self.options.openMode == "docNormalEdit" || self.options.openMode == "docSubmitForm" || self.options.openMode == "docAdmin")) {
            let onWordDataRegionClickFn = self.options.jsFunction_OnWordDataRegionClick.replace("(", "").replace(")", "");
            if (window[onWordDataRegionClickFn]) {
                if (null == selection)
                    return;
                const start = selection.Range.Start;
                const end = selection.Range.End;
                //alert(start + "|" + end);
                if (start != end)
                    return;
                const bookMarkCount = selection.Bookmarks.Count;
                if (bookMarkCount <= 0)
                    return;
                for (let i = 1; i <= bookMarkCount; i++) {
                    const bookmark = selection.Bookmarks.Item(i);
                    if (bookmark.Start <= start && bookmark.End >= end) {
                        const bookmarkName = bookmark.Name,
                            value = safeToString(bookmark.Range.Text).replace(/\x07$/g, "").replace(/\x0d$/g, "");
                        eval(onWordDataRegionClickFn)(bookmarkName, value);
                        break;
                    }
                }

            }
        }
    }


    public executeWithWriteable(func: Function): any {
        const that = this;
        const trackRevisions=that.activeDocument().TrackRevisions;
        if (this._protectionType > -1) {
            this.activeDocument().Unprotect(this._protectPass);
            that.trackRevisions(trackRevisions);
        }

        try {
            const result = func.call(this);
            if (result && result.then && typeof result.then == 'function') {
                result.then(function (value: any) {
                    if (that._protectionType > -1) {
                        that.activeDocument().Protect(that._protectionType, true, that._protectPass);
                        that.trackRevisions(trackRevisions);
                    }
                    return value;
                }, function (error: any) {
                    if (that._protectionType > -1) {
                        that.activeDocument().Protect(that._protectionType, true, that._protectPass);
                        that.trackRevisions(trackRevisions);
                    }
                    return error;
                })
            } else {
                if (that._protectionType > -1) {
                    that.activeDocument().Protect(that._protectionType, true, that._protectPass);
                    that.trackRevisions(trackRevisions);
                }
            }
            return result;
        } catch (e) {
            if (that._protectionType > -1) {
                that.activeDocument().Protect(that._protectionType, true, that._protectPass);
                that.trackRevisions(trackRevisions);
            }
            throw e;
        }

    }

    abstract printPreview(): void;

    abstract closePrintPreview():void;
}